package com.basker.pisces.core;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.NotImplementedException;
import org.springframework.util.Assert;

import com.basker.pisces.core.enums.DataObjectState;
import com.basker.pisces.core.meta.IField;
import com.basker.pisces.core.meta.row.RowContractMeta;
import com.basker.pisces.core.query.meta.FieldDesc;

public class RowContract implements IDataContract, Map<String, Object> {

    private RowContractMeta rowMeta;
    private Object[] rowValues;

    public RowContract(RowContractMeta rowMeta) {
        Assert.notNull(rowMeta, "parameter 'rowMeta' is required");

        this.rowMeta = rowMeta;
        this.initial();
    }

    @Override
    public void beginInit() {

    }

    @Override
    public void clear() {
        throw new NotImplementedException("RowContract's clear method can not be use.");
    }

    @Override
    public void clearOldValues() {

    }

    @Override
    public boolean containsKey(Object key) {
        Assert.notNull(key, "parameter 'key' is required");

        String key0 = this.resolveKeySplit(key);
        return rowMeta.containsField(key0);
    }

    @Override
    public boolean containsValue(Object value) {
        throw new NotImplementedException("RowContract's containsValue method can not be use.");
    }

    @Override
    public void endInit() {

    }

    @Override
    public Set<Entry<String, Object>> entrySet() {
        Collection<IField> fields = this.rowMeta.getFields();
        Set<Entry<String, Object>> listEntry = new LinkedHashSet<Entry<String, Object>>(fields.size(), 1);

        Iterator<IField> it = fields.iterator();
        while (it.hasNext()) {
            IField field = it.next();
            listEntry.add(new RowValueEntry(field.getName(), this.getFieldValue(field)));
        }

        return listEntry;
    }

    @Override
    public Object get(Object key) {
        Assert.notNull(key, "parameter 'key' is required");

        IField field = this.getField(key);
        return this.getFieldValue(field);
    }

    @Override
    public Object getFieldValue(IField field) {
        int order = field.getOrder();

        if (order < 0) {
            return null;
        }

        return this.rowValues[order];
    }

    @Override
    public Object getOldValue(String name) {
        return null;
    }

    @Override
    public Object getPkValue() {
        return null;
    }

    @Override
    public DataObjectState getState() {
        return DataObjectState.UnChanged;
    }

    @Override
    public boolean isChanged(String fieldName) {
        return false;
    }

    @Override
    public boolean isEmpty() {
        return this.size() == 0;
    }

    @Override
    public boolean isFromDB() {
        return true;
    }

    @Override
    public boolean isFromDB(String tableName) {
        return true;
    }

    @Override
    public boolean isInInitial() {
        return false;
    }

    @Override
    public boolean isReadOnly() {
        return true;
    }

    @Override
    public Set<String> keySet() {
        return rowMeta.getFieldNames();
    }

    @Override
    public Object put(String key, Object value) {
        Assert.notNull(key, "parameter 'key' is required");

        IField field = this.getField(key);

        Object oldValue = field.getValue(this);
        this.setFieldValue(field, value);

        return oldValue;
    }

    @Override
    public void putAll(Map<? extends String, ? extends Object> otherMap) {
        Iterator<?> it = otherMap.entrySet().iterator();
        while (it.hasNext()) {
            @SuppressWarnings("unchecked")
            Entry<String, Object> entry = (Entry<String, Object>) it.next();
            this.put(entry.getKey(), entry.getValue());
        }

    }

    @Override
    public void recordOldValue(String fieldName, Object oldValue) {

    }

    @Override
    public Object remove(Object key) {
        throw new NotImplementedException("RowContract's remove method can not be use.");
    }

    @Override
    public void setFieldValue(IField field, Object value) {
        int order = field.getOrder();

        if (order < 0) {
            return;
        }

        this.rowValues[order] = value;

    }

    @Override
    public void setFromDB(boolean fromDB) {

    }

    @Override
    public void setFromDB(String tableName, boolean fromDB) {

    }

    @Override
    public void setReadOnly(boolean readOnly) {

    }

    @Override
    public void setState(DataObjectState state) {

    }

    @Override
    public int size() {
        return rowMeta.getFields().size();
    }

    @Override
    public String toString() {
        return Arrays.toString(this.rowValues);
    }

    @Override
    public Collection<Object> values() {
        return Arrays.asList(this.rowValues);
    }

    private IField getField(Object key) {
        String key0 = this.resolveKeySplit(key);
        IField field = rowMeta.getField(key0);

        if (field == null) {
            throw new IllegalArgumentException("parameter '" + key0 + "' is not found in '" + this.toString() + "'");
        }

        return field;
    }

    private void initial() {
        int size = this.rowMeta.getFields().size();
        this.rowValues = new Object[size];
    }

    private String resolveKeySplit(Object key) {
        if (key instanceof String) {
            String key0 = (String) key;

            return FieldDesc.resolveSplit(key0);
        }

        return key.toString();
    }

    private final static class RowValueEntry implements Entry<String, Object> {
        private final String key;
        private Object value;

        public RowValueEntry(String key, Object value) {
            this.key = key;
            this.value = value;
        }

        @Override
        public String getKey() {
            return key;
        }

        @Override
        public Object getValue() {
            return value;
        }

        @Override
        public Object setValue(Object value) {
            Object old = this.value;
            this.value = value;
            return old;
        }

        @Override
        public String toString() {
            return this.key + ":" + this.value;
        }

    }

}
